package com.ldy.gkrpc.client;

import com.ldy.gkrpc.codec.Decoder;
import com.ldy.gkrpc.codec.Encoder;
import com.ldy.gkrpc.codec.JSONDecoder;
import com.ldy.gkrpc.codec.JSONEncoder;
import com.ldy.gkrpc.proto.Request;
import com.ldy.gkrpc.proto.Response;
import com.ldy.gkrpc.proto.ServiceDescriptor;
import com.ldy.gkrpc.transport.TransportClient;
import com.ldy.gkrpc.transport.impl.HTTPTransportClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @Description 调用远程服务的代理类
 * @Author
 * @Date 2020/4/15 20:19
 **/
@Slf4j
public class RemoteInvocationHandler implements InvocationHandler {
    private Encoder encoder;

    private Decoder decoder;

    private TransportSelector selector;

    private Class clazz;
    public RemoteInvocationHandler(Class clazz , Encoder encoder, Decoder decoder,TransportSelector transportSelector){
        this.clazz =clazz;
        this.encoder =encoder;
        this.decoder = decoder;
        this.selector =transportSelector;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Request request = new Request();
        request.setServiceDescrptor(ServiceDescriptor.from(clazz,method));
        request.setParamters(args);

        Response response = invokeRemote(request);
        if(response == null || response.getCode() !=0){
            throw new IllegalStateException("fail to invoke remote:"+response.getMessage());
        }
        return response.getData();

    }

    private Response invokeRemote(Request request) {
        TransportClient transportClient = null;
        Response response =new Response();
        try {
            transportClient = selector.select();
            byte[] encode = encoder.encode(request);
            InputStream write = transportClient.write(new ByteArrayInputStream(encode));
            byte[] bytes = IOUtils.readFully(write, write.available());
            response = decoder.decode(bytes, Response.class);
        } catch (IOException e) {
            log.error(" rpc invoke fail {}",e.getMessage());
            response.setMessage(e.getMessage());
            response.setCode(1);
        } finally {
            if(transportClient!=null){
                this.selector.release(transportClient);
            }
        }
        return response;
    }
}
